#-*- coding: utf-8 -*-
"""
PythonTextureAssembler v1 - Assembles textures into one file. It can be used recursively.
Basic original idea by Julien, implementation and improvements by Guillaume.

All images must share the same extension, files must share a base name (like World1.png, World2.png...).
A Python matrix defines how to assemble all images together.

The file containing the matrix must have in its first line the common extension OR a prefix (by default, the folder name), 
the extension and (optionally) the filling color (used as rows aren't the same size).
Other lines are considered to be part of the matrix (simply parsed via eval). Don't forget square brackets around it...
"""

import umage #fonctions déjà faites (TP)
import copy #pour deepcopy() vu que la matrice est détruite à chaque fois...
import argparse #lib très puissante alors pourquoi s'en priver...
import os #pour explorer les dossiers

defaultColor = (255, 0, 255)


def debug_img(img):
    for r in img:
        print r

def assemble(fn, mat, ext, v):
    """
    Assemble différents fichiers selon la matrice donnée en paramètre.
    Attention : la matrice est détruite. Pour la réutiliser, il faut en faire une deepcopy.
    """
    pic=[]
    for id_e, e in enumerate(mat): #pour chaque ligne,
        if v: print "Matrix's row %d" % id_e
        #on charge les morceaux
        for id, n in enumerate(e):
            #on crée un nom de fichier, par exemple "World1.png"
            final = "%s%d.%s" % (fn, n, ext)
            e[id] = umage.load(final)
            if v: print "  Loaded: %s..." % final
        if len(e)>0:
            #il y a quelque chose à faire
            block = e[0] #première tuile
            if v: print "  Column 1"
            #pour chaque suivante dans la ligne,
            for id in range(1, len(e)):
                if v: print "  Column %d" % (id+1)
                #pas grave si on massacre e[0] !
                #pour chaque ligne, on l'ajoute au bloc de base
                for y, row in enumerate(e[id]):
                    block[y].extend(row)
            #référence perdue, pas un problème. Faut pas oublier que la boucle fera l'équivalent d'un e=qqch,
            #donc e pointera vers autre chose mais l'objet ne sera pas détruit (car référencé ailleurs)
            pic.extend(block)
    return pic

def adjust(pic, color, v):
    """
    Vérifie que pic est bien un rectangle et corrige si nécessaire en insérant des pixels color.
    Attention : pic est directement modifiée. Avantage : ça va bien plus vite.
    """
    if v: print "Checking whether there's anything to be adjusted..."
    old_max = max = 0 #on cherche la ligne la plus longue
    for r in pic:
        if len(r)>max:
            old_max = max
            max = len(r)
    if old_max==0:
        if v: print "  Nothing to do !"
    else:
        if v: print "Filling rows with %s when needed..." % str(color)
        for y in range(len(pic)):
            #on ajuste la taille si nécessaire
            x = len(pic[y])
            if x<maxi:
                #on ajoute "ce qu'il faut" de manière VISIBLE
                if v: print "  Row %d" % (y+1)
                pic[y].extend([color] * (max-x))

def parseMatrix(folder, filename):
    """
    Lit filename et parse son contenu.
    Format attendu : 
    * Première ligne :
      * format OU
      * préfixe, format OU
      * préfixe, format, couleur (HTML)
    * le reste représente une matrice Python
    """
    #FIXME passoire niveau de vue sécurité (eval) !!
    #IOError - impossible de lire le fichier...
    #SyntaxError, IndexError, ValueError - structure invalide
    f = file(os.path.join(folder, filename), 'r')
    ln = f.readlines()
    f.close()
    first = ln[0][:-1].split(',')
    pre = os.path.basename(folder)
    col = defaultColor
    if len(first)==1:
        ext = first[0].strip()
    elif len(first)>=2:
        pre = first[0]
        ext = first[1].strip()
        if len(first)>2:
            col = first[2].strip()
            if col[0]!='#' or len(col)!=7:
                raise ValueError('Invalid color format: '+col)
            else:
                #on la convertit directement en un tuple...
                col = (int(col[1:3], 16), int(col[3:5], 16), int(col[5:], 16))
    mat = eval(''.join(ln[1:]))
    return pre, ext, mat, col

def explore(folder, rec):
    """
    Explore le dossier folder récursivement jusqu'à un certain niveau.
    -1 = profondeur infinie, n = profondeur de n, 0 = aucune profondeur
    Pour l'instant, ce n'est pas géré en ligne de commande mais ça pourrait peut-être servir...
    Si une erreur s'est produite, elle est affichée et la fonction retourne True (sinon False).
    """
    if args.verbose>=1: print ">>> Entering: %s" % folder
    if rec != 0:
        lst = os.listdir(folder)
        for ent in lst:
            full = os.path.join(folder, ent)
            if not os.path.isfile(full):
                if explore(full, rec-1 if rec!=-1 else -1):
                    #on fait remonter les erreurs
                    return True
    if args.verbose>=1: print ">>> Reading matrix: %s" % folder
    mfn = args.mat if args.mat!=None else os.path.basename(folder)+'.mat'
    try:
        pre, ext, mat, col = parseMatrix(folder, mfn)
    except IOError as e:
        print e
        return True
    except Exception as e: #les autres...
        print "%s: %s" % (folder, str(e))
        return True
    try:
        if args.verbose>=1: print ">>> Processing matrix..."
        base = os.path.join(folder, pre)
        if args.out==None:
            out = base
        else:
            out = os.path.join(folder, args.out)
            out = out.replace('%', os.path.basename(folder))
        output = assemble(base, mat, ext, args.verbose>=2)
        adjust(output, col, args.verbose>=2)
        if len(output)>0:
            if args.verbose>=1:
                print ">>> Saving: %s.%s" % (out, ext)
            else:
                print "%s.%s" % (out, ext)
            umage.save(output, out, ext)
            if args.verbose>=1: print ">>> Done: %s" % folder
        return False #tout s'est bien passé
    except Exception as e:
        print e
        return True


if __name__=='__main__':
    parser = argparse.ArgumentParser(description=unicode(__doc__, 'utf8'))
    parser.add_argument('target', nargs='+')
    parser.add_argument('--matrix', '-M', dest='mat', help="matrix file (default=%%, %%=current directory+'.mat')")
    parser.add_argument('--out', '-O', help="output file (default=%%, %%=current directory). Extension given by the matrix file.")
    parser.add_argument('--recursive', '-r', action='store_const', const=-1, default=0, help="explore subdirectories as well")
    parser.add_argument('--verbose', '-v', action='count', default=0, help="verbose mode (-v=1, -vv=2)")
    args = parser.parse_args()
    for t in args.target:
        if explore(os.path.abspath(t), args.recursive):
            exit(1)
    exit(0)
